#include "TileLoader.h"
#include "CachedTexture.h"

bool bRunThread = true;

CRITICAL_SECTION
	TileLoader::LoadingCS;
std::queue<TileLoader::LOADINGPARAM>
	TileLoader::queue;

void read_callback( png_structp png_ptr, png_bytep to_lib, png_size_t lenght ) {
	FILE *file = (FILE*)png_ptr->io_ptr;
	fread( to_lib, 1, lenght, file );
}

TileLoader::TileLoader() {
	dwLoading = 0;
	hLoading = NULL;

	InitializeCriticalSection( &LoadingCS );
	hLoading = CreateThread( NULL, 2048, LoadingProc, this, 0, &dwLoading );
}

TileLoader::~TileLoader() {
	bRunThread = false;
	TerminateThread( hLoading, 0xFF );
	hLoading = NULL;
	DeleteCriticalSection( &LoadingCS );
}

bool TileLoader::LoadTile( TerrainManager *TM, TILEDESC1 *tile ) {

	if( queue.size() == MAXLOADQUEUE )	return false;

	for( UINT j = 0; j < queue.size(); j++ )
		if( queue._Get_container()[j].td == tile )
			return false;

	LOADINGPARAM lp;
	lp.TM = TM;
	lp.td = tile;

	queue.push( lp );

	return true;
}

DWORD WINAPI TileLoader::LoadingProc( void *data ) {
	static const UINT64 TILESIZE = 32896;

	TileLoader *tl = (TileLoader*)data;
	const DWORD IdleTime = 30;//1000/cfg->TileLoadingFrequency;
	bool load;
	LOADINGPARAM qd;

	while( bRunThread ) {
		Sleep( IdleTime );

		EnterCriticalSection( &LoadingCS );
		if( load = (tl->queue.size() != 0) ) {
			qd = tl->queue.front();
			tl->queue.pop();
		}
		LeaveCriticalSection( &LoadingCS );

		if( load ) {
			DWORD
				remove_flag = 0,
				add_flag = 0;
			INT16 A[2];
			UINT tidx, ofs;
			TILEDESC1 *td = qd.td;
			ID3D11ShaderResourceView *tex = NULL, *ltex = NULL, *ntex = NULL, *htex = NULL, *ctex = NULL;

/*			if( td->flags & REFRESH_TEX ) {		[unfinished]
				//load texture
				tidx = (UINT)td->tex;
				if( tidx != NOTILE ) {
					ofs = (td->flags & OLDIDX ? tidx*TILESIZE : tidx);
					if( !LoadTexture( qd.TM->tex_fname, ofs, &tex ) )
						tex = NULL;
				}
				remove_flag |= REFRESH_TEX;

				EnterCriticalSection( &LoadingCS );
				td->tex = tex;			//write texture for new month
				REL( td->tex_old );		//delete texture for passed month
				td->flags &= ~remove_flag;
				queue_out++;
				nqueue--;
				LeaveCriticalSection( &LoadingCS );
				continue;
			}
*/
			if( td->flags & LOAD_TEX ) {
				//load texture
				tidx = td->tex->GetOffset();
				if( tidx != NOTILE ) {
					ofs = (td->flags & OLDIDX ? tidx*TILESIZE : tidx);
					if( !LoadTexture( qd.TM->tex_fname, ofs, &tex ) )
						tex = NULL;
				}

				//load specular mask
				if( ((td->flags & 3) == 3) || (td->flags & 4) ) {
					tidx = (UINT)td->ltex;
					if( tidx != NOTILE ) {
						ofs = (td->flags & OLDIDX ? tidx*TILESIZE : tidx);
						if( !LoadTexture( qd.TM->spec_fname, ofs, &ltex ) )
							ltex = NULL;
					}
				}
				remove_flag |= LOAD_TEX;
			}

			//load normal map
			if( td->flags & LOAD_NORM ) {
				tidx = (UINT)td->ntex->GetOffset();
				if( tidx != NOTILE ) {
					if( !LoadTexture( qd.TM->norm_fname, tidx, &ntex ) )
						ntex = NULL;
				}
				remove_flag |= LOAD_NORM;
			}

			//load land cover map
			if( td->flags & LOAD_LCOVER ) {
				tidx = (UINT)td->ctex;
				if( tidx != NOTILE ) {
					if( !LoadLandCoverMap( qd.TM->lcover_fname, tidx, &ctex ) )
						ctex = NULL;
				}
				remove_flag |= LOAD_LCOVER;
			}

			EnterCriticalSection( &LoadingCS );
			CachedTexture::Init(td->tex, tex);
			/*if( tex || (td->tex && (DWORD)td->tex->GetOffset() == NOTILE ))
			{
				if (td->tex)
				{
					if (tex)
					{
						td->tex->Load(tex);
					}
				}
				else
				{
					if (tex)
					{
						td->tex.reset(new CachedTexture(tex, true));
					}
				}
			}*/
			if( ltex || (DWORD)td->ltex == NOTILE )	td->ltex = ltex;
			CachedTexture::Init(td->ntex, ntex);
			//if( ntex || (DWORD)td->ntex == NOTILE )	td->ntex = ntex;
			if( ctex || (DWORD)td->ctex == NOTILE )	td->ctex = ctex;
			td->flags &= ~remove_flag;
			td->flags |= add_flag;
			LeaveCriticalSection( &LoadingCS );
		}
	}
	return 0;
}

bool TileLoader::LoadTexture( char *fname, UINT64 ofs, ID3D11ShaderResourceView **out ) {
	FILE *file;
	DDS_HEADER ddsHeader;
	HRESULT hr;
	DWORD key;
	BYTE *data;

	if( !fname || !(file = fopen( fname, "rb" )) )
		return false;

	_fseeki64( file, ofs, SEEK_SET );		//for textures larger than 2 Gb
	fread( &key, 4, 1, file );
	if( key != MAKEFOURCC( 'D', 'D', 'S', ' ' ) )
		return false;

	fread( &ddsHeader, sizeof(DDS_HEADER), 1, file );
	DWORD bufsize = ddsHeader.dwPitchOrLinearSize;
	bufsize += ddsHeader.dwMipMapCount ? bufsize/2 : 0;
	bufsize += (4 + sizeof(DDS_HEADER));
	data = new BYTE [bufsize+1];
	fseek( file, ofs, SEEK_SET );
	fread( data, 1, bufsize, file );
	fclose( file );

	D3DX11_IMAGE_INFO IInfo;
	D3DX11_IMAGE_LOAD_INFO LInfo;

	D3DX11GetImageInfoFromMemory( data, bufsize, NULL, &IInfo, &hr );
	if( hr != S_OK ) {
		delete [ ] data;
		return false;
	}

	LInfo.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	LInfo.CpuAccessFlags = 0;
	LInfo.Depth = 1;
	LInfo.Filter = D3DX11_FILTER_NONE;
	LInfo.FirstMipLevel = 0;
	LInfo.Format = IInfo.Format;
	LInfo.MipFilter = D3DX11_FILTER_NONE;
	LInfo.MipLevels = 1;//ddsHeader.dwMipMapCount ? ddsHeader.dwMipMapCount : 1;
	LInfo.MiscFlags = 0;
	LInfo.pSrcInfo = &IInfo;
	LInfo.Usage = D3D11_USAGE_IMMUTABLE;
	LInfo.Width = IInfo.Width;
	LInfo.Height = IInfo.Height;

	HR( D3DX11CreateShaderResourceViewFromMemory( Dev, data, bufsize, &LInfo, NULL, out, NULL ) );
#if _DEBUG
	{
		char buffer[200];
		buffer[199] = '\0';
		sprintf(buffer, "[TileLoader::LoadTexture]%d-%s", ofs, fname);
		if (*out)
			(*out)->SetPrivateData(WKPDID_D3DDebugObjectName, 200, buffer);
	}
#endif
	delete [ ] data;
	if( hr != S_OK )
		return false;
	return hr == S_OK ? true : false;
}

bool TileLoader::LoadLandCoverMap( char *fname, UINT32 ofs, ID3D11ShaderResourceView **out ) {
	UINT j;
	BYTE *data;
	FILE *file;

	if( !fname || !(file = fopen( fname, "rb" )) )
		return false;

	fseek( file, ofs, SEEK_SET );
	png_structp png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL );
	png_infop info_ptr = png_create_info_struct( png_ptr );
	png_set_read_fn( png_ptr, file, read_callback );

	png_read_info( png_ptr, info_ptr );

	BYTE MAP[16384], *rows[128];
	for( UINT i = 0; i < 128; i++ )		rows[i] = &MAP[i*128];
	png_read_image( png_ptr, rows );

	png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
	png_destroy_info_struct( png_ptr, &info_ptr );
	fclose( file );

	//create resource
	D3D11_TEXTURE2D_DESC tdesc;
	D3D11_SUBRESOURCE_DATA sdata;
	ID3D11Texture2D *tex;

	memset( &tdesc, 0, sizeof(tdesc) );
	tdesc.ArraySize = 1;
	tdesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	tdesc.CPUAccessFlags = 0;
	tdesc.Format = DXGI_FORMAT_R8_UINT;
	tdesc.Height = 128;
	tdesc.Width = 128;
	tdesc.MipLevels = 1;
	tdesc.MiscFlags = 0;
	tdesc.SampleDesc.Count = 1;
	tdesc.Usage = D3D11_USAGE_IMMUTABLE;
	
	memset( &sdata, 0, sizeof(sdata) );
	sdata.pSysMem = MAP;
	sdata.SysMemPitch = 128;

	HR( Dev->CreateTexture2D( &tdesc, &sdata, &tex ) );

	D3D11_SHADER_RESOURCE_VIEW_DESC srvdesc;
	memset( &srvdesc, 0, sizeof(srvdesc) );
	srvdesc.Format = DXGI_FORMAT_R8_UINT;
	srvdesc.Texture2D.MipLevels = 1;
	srvdesc.Texture2D.MostDetailedMip = 0;
	srvdesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;

	HR( Dev->CreateShaderResourceView( tex, &srvdesc, out ) );
#if _DEBUG
	{
		char buffer[200];
		buffer[199] = '\0';
		sprintf(buffer, "[TileLoader::LoadLandCoverMap]%d-%s", ofs, fname);
		if (tex)
			tex->SetPrivateData(WKPDID_D3DDebugObjectName, 200, buffer);
		if (*out)
			(*out)->SetPrivateData(WKPDID_D3DDebugObjectName, 200, buffer);
	}
#endif
	REL( tex );
	return true;
}
